
#include "vty_dft_command.hpp"
#include "command_manager.hpp"
#include "vty_manager.hpp"

#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>

namespace io = boost::iostreams;

#define  SHOW_HELP "show system information\n"
#define  TIME_HELP "crrent system times\n"
#define END_HELP "Ends your configuration session by exiting to privileged mode."

#define NO_HELP     "negate a command or set its defaults\n"

#define TMP_HELP "tmp string\n"

#define ALAR_HELP "system alarm messages\n"
#define COPY_HELP "copy file\n"
#define CLEAR_HELP "remove information\n"
#define RUN_CFG_HELP "current system configuration\n"

#define IPV4_HELP "ip information\n"
#define ICMP_HELP "Internet Control Message Protocol\n"
#define ICMP_ECHO_HELP "ICMP Echo Message\n"

#define VIEW_MODE_STR "VIEW"
#define EXEC_MODE_STR "ENABLE"
#define CFG_MODE_STR "CONFIG"

static const struct mode _auth("AUTH ", "passwd:");
static const struct mode _auth_enable("AUTH_ENABLE", "passwd:");
static const struct mode _view("VIEW", ">");
static const struct mode _exec("ENABLE", "#");

static int excute_popen(vty_ptr vty, const char* cmdline);
static int test_dialog_func(vty_ptr vty, const std::string inputline);
int exit_vty(vty_ptr vty, const std::string& command, string_vector& args);
int chg_priv_mode(vty_ptr vty, const std::string& command, string_vector& args);
int show_time(vty_ptr vty, const std::string& command, string_vector& args);
int test_dialog(vty_ptr vty, const std::string& command, string_vector& args);
int test_pty(vty_ptr vty, const std::string& command, string_vector& args);
int exec_telnet(vty_ptr vty, const std::string& command, string_vector& args);
int exec_ssh(vty_ptr vty, const std::string& command, string_vector& args);
int exec_shell(vty_ptr vty, const std::string& command, string_vector& args);

int end_function(vty_ptr vty, const std::string& command, string_vector& args);
int ctrl_alarm(vty_ptr vty, const std::string& command, string_vector& args);
int show_alarm(vty_ptr vty, const std::string& command, string_vector& args);
int clear_alarm(vty_ptr vty, const std::string& command, string_vector& args);
int copy_alarm(vty_ptr vty, const std::string& command, string_vector& args);

int ctrl_icmp_echo(vty_ptr vty, const std::string& command, string_vector& opts);

int show_running_config(vty_ptr vty, const std::string& command, string_vector& opts);
int test_dialog_func(vty_ptr vty, const std::string inputline);

void vty_dft_command::build() 
{
    _manager.add_mode(_auth._name, _auth._prompt, true);
    _manager.add_mode(_auth_enable._name, _auth_enable._prompt, true);
    _manager.add_mode(_view._name, _view._prompt);
    _manager.add_mode(_exec._name, _exec._prompt);
    _manager.add_mode(CFG_MODE_STR, "(config)#");


    _manager.add_command(_view._name.c_str(), 
            "enable",  "turn on privileged mode command\n", chg_priv_mode);
    _manager.add_command(_exec._name.c_str(), "configure terminal",  
            "configuration mode\n"
            "executes configuration commands from the terminal.\n"
            , chg_priv_mode);

    _manager.add_command(_view._name.c_str(), "show time", SHOW_HELP TIME_HELP, show_time);
    _manager.add_command(EXEC_MODE_STR,
            "exit", "exit vty connection\n", exit_vty);
    _manager.add_command(VIEW_MODE_STR,
            "exit", "exit vty connection\n", exit_vty);

    _manager.add_command(_view._name.c_str(), "test dialog", 
            "test system functions\n" "dialog function", test_dialog);

    _manager.add_command(_exec._name.c_str(), 
            "telnet (HOSTNAME|A.B.C.D)", 
            "telnet client\n" "hostname\n" "ip address\n", 
            exec_telnet);
    _manager.add_command(_exec._name.c_str(), 
            "telnet (HOSTNAME|A.B.C.D) <1-65536>", 
            "telnet client\n" "hostname\n" "ip address\n" "port number\n", 
            exec_telnet);

    _manager.add_command(_exec._name.c_str()
            ,"show running-config" , SHOW_HELP RUN_CFG_HELP,
            show_running_config);
    _manager.add_command(_exec._name.c_str()
            ,"alarm" ,ALAR_HELP, ctrl_alarm);
    _manager.add_command(_exec._name.c_str()
            ,"no alarm" ,NO_HELP ALAR_HELP, ctrl_alarm);
    _manager.add_command(_exec._name.c_str()
            ,"show alarm" ,SHOW_HELP ALAR_HELP, show_alarm);
    _manager.add_command(_exec._name.c_str()
            ,"clear alarm" ,CLEAR_HELP ALAR_HELP, clear_alarm);
    _manager.add_command(_exec._name.c_str()
            ,"copy alarm" ,COPY_HELP ALAR_HELP, copy_alarm);

    _manager.add_command(_exec._name.c_str()
            ,"ssh (HOSTNAME|A.B.C.D) USER"
            ,"ssh client\n" "hostname\n" "ip address\n"  "user id\n"
            ,exec_ssh);

    _manager.add_command(_exec._name.c_str()
            ,"ssh (HOSTNAME|A.B.C.D) USER <1-65536>"
            ,"ssh client\n" "hostname\n" "ip address\n"  "user id\n" "port number\n"
            ,exec_ssh);

    _manager.add_command(_exec._name.c_str(), "show time", SHOW_HELP TIME_HELP, show_time);

    _manager.add_command(_exec._name.c_str(), "backdoor", "turn on linux shell mode\n", 
            exec_shell);


    _manager.add_command(EXEC_MODE_STR, "end", END_HELP, end_function);
    _manager.add_command(CFG_MODE_STR, "end", END_HELP, end_function);
            
            
    _manager.add_command(CFG_MODE_STR, "icmp echo (all|broadcasts)",
            ICMP_HELP "enable icmp response\n" "all type\n"
            "boaradcast type\n", ctrl_icmp_echo);
    _manager.add_command(CFG_MODE_STR, "no icmp echo (all|broadcasts)",
            NO_HELP ICMP_HELP "enable icmp response\n" "all type\n"
            "boaradcast type\n", ctrl_icmp_echo);
};

static int excute_popen(vty_ptr vty, const char* cmdline)
{
    FILE* pipe = popen(cmdline, "r");

    io::stream_buffer<io::file_descriptor_source> fpstream(fileno(pipe));
    std::istream in(&fpstream);

    std::cout << std::endl;
    std::string line;
    while (in)
    {
        std::getline(in, line);
        line.append(VTY_NEWLINE);
        vty->output(line);  /* FIXME: fix newline handling.. */
        std::cout << line << std::endl;
    }
	command_manager::instance()->show_agent_runcfg(vty);
    return 0;
};

int get_proc_int(const std::string& path)
{
    char cmdline[1024];
    sprintf(cmdline, "cat %s", path.c_str());
    FILE* pipe = popen(cmdline, "r");

    io::stream_buffer<io::file_descriptor_source> fpstream(fileno(pipe));
    std::istream in(&fpstream);
    std::string line;
    std::getline(in, line);
    std::cout << "check: " << line << std::endl;
    return atoi(line.c_str());
}

int show_running_config(vty_ptr vty, const std::string& command, string_vector& opts)
{
    vty->output("!%s", VTY_NEWLINE);
    vty->output("!Current configuration:%s", VTY_NEWLINE);
    vty->output("!%s", VTY_NEWLINE);
    if (vty_manager::instance()->get_alarm_flag() == false) {
        vty->output(" no aram%s", VTY_NEWLINE);
    }

    vty->output("!%s", VTY_NEWLINE);
    if (get_proc_int("/proc/sys/net/ipv4/icmp_echo_ignore_all") == 0) {
        vty->output(" icmp echo all%s", VTY_NEWLINE);
    }
    if (get_proc_int("/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts") == 0) {
        vty->output(" icmp echo broadcasts%s", VTY_NEWLINE);
    }
    vty->output("!%s", VTY_NEWLINE);
	command_manager::instance()->show_agent_runcfg(vty);
    vty->output("!%s", VTY_NEWLINE);

    return 0;
}

int ctrl_icmp_echo(vty_ptr vty, const std::string& command, string_vector& opts)
{
    char ctrstr[256];
    std::string ipv4_icmp_proc("/proc/sys/net/ipv4/");
    char buf[1024];
    int typeidx = 2;

    if (opts[0].compare("no") == 0) {
        sprintf(ctrstr, "echo 1 > "); typeidx = 3;
    } else {
        sprintf(ctrstr, "echo 0 > "); typeidx = 2;
    }

    if(opts[typeidx].compare("all") == 0) {
        ipv4_icmp_proc.append("icmp_echo_ignore_all");
    } else if (opts[typeidx].compare("broadcasts") == 0) {
        ipv4_icmp_proc.append("icmp_echo_ignore_broadcasts");
    } else {
        return -1;
    }
    sprintf(buf, "%s %s", ctrstr, ipv4_icmp_proc.c_str());
    PDBG("check: %s\n", buf);
    system(buf);
    return 0;
}

int ctrl_alarm(vty_ptr vty, const std::string& command, string_vector& opts)
{
	PDBG("opt_num: %d\n", opts.size());
	if (opts.empty()) { 
        vty->output("$$ invalid args number%s", VTY_NEWLINE);
		return 0;
	}
	
	if (opts[0].compare("no") == 0) {
		vty_manager::instance()->ctrl_alarm(false);
	} else {
		vty_manager::instance()->ctrl_alarm(true);
	}
	return 0;
}

int show_alarm(vty_ptr vty, const std::string& command, string_vector& opts)
{
	std::string fname = vty_manager::instance()->alarm_file_name();
	std::ifstream fs(fname.c_str());
	std::string line;
	while (getline(fs, line)) {
		line.append(VTY_NEWLINE);
		vty->output(line);
	}
	fs.close();
	return 0;
}

int clear_alarm(vty_ptr vty, const std::string& command, string_vector& opts)
{
    std::string fname = vty_manager::instance()->alarm_file_name();
    char tmp[1024];
    sprintf(tmp, "echo \"\" > %s", fname.c_str());  /* FIXME */
    system(tmp);
	return 0;
}

int copy_alarm(vty_ptr vty, const std::string& command, string_vector& opts)
{
    std::string fname = vty_manager::instance()->alarm_file_name();
    std::string dstname("/tmp/");
    dstname.append(opts[2]);
    char tmp[1024];
    sprintf(tmp, "copy %s %s", fname.c_str(), dstname.c_str());  /* FIXME */
    system(tmp);
	return 0;
}


int exit_vty(vty_ptr vty, const std::string& command, string_vector& args)
{
    vty->stop();
    return 0;
};

int end_function(vty_ptr vty, const std::string& command, string_vector& args)
{
    std::string mode = vty->current_mode();
    if (mode.compare(EXEC_MODE_STR) == 0)
        vty->change_mode(VIEW_MODE_STR);
    else if (mode.compare(CFG_MODE_STR) == 0)
        vty->change_mode(EXEC_MODE_STR);
    return 0;
}

int chg_priv_mode(vty_ptr vty, const std::string& command, string_vector& args)
{
    std::string mode = vty->current_mode();
    if (mode.compare("VIEW") == 0)
        vty->change_mode(_auth_enable._name);
    else if (mode.compare(EXEC_MODE_STR) == 0 && args[1].compare("terminal") == 0)
        vty->change_mode(CFG_MODE_STR);
    return 0;
};

int show_time(vty_ptr vty, const std::string& command, string_vector& args)
{
    excute_popen(vty, "date");
    return 0;
};

int test_dialog(vty_ptr vty, const std::string& command, string_vector& args)
{
    vty->start_dialog(test_dialog_func);
    return 0;
};

int test_pty(vty_ptr vty, const std::string& command, string_vector& args)
{
    //vty->excute_pty("hello");
    return 0;
};

int exec_telnet(vty_ptr vty, const std::string& command, string_vector& args)
{
    string_vector opts;
    size_t args_num = args.size();
    
    PDBG("arg num: %d\n", (int)args_num);
    if (args_num < 2) {
        vty->output("$$ invalid args number%s", VTY_NEWLINE);
        return 0;
    }

    opts.push_back("telnet");
    opts.push_back(args[1]);
    if (args_num == 3) 
        opts.push_back(args[2]);
    vty->excute_pty(opts);
    return 0;
}

int exec_ssh(vty_ptr vty, const std::string& command, string_vector& args)
{
    string_vector opts;
    size_t args_num = args.size();

    opts.push_back("ssh");
    if (args_num == 3) {
        opts.push_back("-l");
        opts.push_back(args[2]);
        opts.push_back(args[1]);
    } else if (args_num == 4) {
        opts.push_back("-l");
        opts.push_back(args[2]);
        opts.push_back(args[1]);
        opts.push_back("-p");
        opts.push_back(args[3]);
    } else {
        vty->output("$$ invalid args number%s", VTY_NEWLINE);
        return 0;
    }
    vty->excute_pty(opts);
    return 0;
}


int exec_shell(vty_ptr vty, const std::string& command, string_vector& args)
{
    string_vector opts;
    
    opts.push_back("/bin/bash");
    vty->excute_pty(opts);
    return 0;
}


int test_dialog_func(vty_ptr vty, const std::string inputline)
{
    if (inputline.compare("yes") == 0) {
        vty->output("your answer is %s%s", inputline.c_str(), VTY_NEWLINE);
        vty->stop_dialog();
    } else if (inputline.compare("no") == 0) {
        vty->output("your answer is %s%s", inputline.c_str(), VTY_NEWLINE);
        vty->stop_dialog();
    } else  {
        vty->output("do you wanna restart system? [No/yes]: ");
    }
    return 0;
};


